/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2021. All rights reserved.
 */

package com.baidu.process.cloud.apigateway.sdk.utils;

import com.baidu.process.cloud.apigateway.sdk.utils.AccessService;
import com.baidu.process.cloud.apigateway.sdk.utils.Request;
import com.baidu.process.cloud.apigateway.sdk.utils.UnknownHttpMethodException;
import com.baidu.process.cloud.sdk.auth.signer.Signer;
import com.baidu.process.cloud.sdk.http.HttpMethodName;

import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;

public class AccessServiceImpl extends AccessService {
    private static final String UTF8 = "UTF-8";
    private static final String CHAR_SET_NAME_ISO = "ISO-8859-1";

    public AccessServiceImpl(String ak, String sk) {
        super(ak, sk);
    }

    public AccessServiceImpl(String ak, String sk, String messageDigestAlgorithm) {
        super(ak, sk, messageDigestAlgorithm);
    }

    @Override
    public HttpRequestBase access(String url, Map<String, String> headers, String content, HttpMethodName httpMethod)
            throws Exception {
        com.baidu.process.cloud.apigateway.sdk.utils.Request request = new Request();
        request.setAppKey(ak);
        request.setAppSecrect(sk);
        request.setMethod(httpMethod.name());
        request.setUrl(url);
        for (Map.Entry<String, String> map : headers.entrySet()) {
            request.addHeader(map.getKey(), map.getValue());
        }
        request.setBody(content);

        Signer signer = new Signer(messageDigestAlgorithm);
        // Sign the request, and the request will change after the signing.
        signer.sign(request);
        // Make a request that can be sent by the HTTP client.
        HttpRequestBase httpRequestBase = createRequest(url, null, content, httpMethod);
        // Put the header of the signed request to the new request.
        for (Map.Entry<String, String> map : request.getHeaders().entrySet()) {
            if (map.getKey() == null) {
                continue;
            }
            if (map.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
                continue;
            }
            if (map.getValue() == null) {
                continue;
            }
            httpRequestBase.addHeader(map.getKey(), new String(map.getValue().getBytes(UTF8), CHAR_SET_NAME_ISO));
        }

        return httpRequestBase;
    }

    @Override
    public HttpRequestBase access(String url, Map<String, String> headers, InputStream content, Long contentLength,
            HttpMethodName httpMethod) throws Exception {
        String body = "";
        if (content != null) {
            ByteArrayOutputStream result = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = content.read(buffer)) != -1) {
                result.write(buffer, 0, length);
            }
            body = result.toString(UTF8);
        }
        return access(url, headers, body, httpMethod);
    }

    /**
     * Make a request that can be sent by the HTTP client.
     *
     * @param url specifies the API access path.
     * @param header specifies the header information to be added.
     * @param content specifies the body content to be sent in the API call.
     *            optional.
     * @param httpMethod specifies the HTTP method to be used.
     * @return specifies the request that can be sent by an HTTP client.
     */
    private static HttpRequestBase createRequest(String url, Header header, String content, HttpMethodName httpMethod) {
        HttpRequestBase httpRequest;
        if (httpMethod == HttpMethodName.POST) {
            HttpPost postMethod = new HttpPost(url);

            if (content != null) {
                StringEntity entity = new StringEntity(content, StandardCharsets.UTF_8);
                postMethod.setEntity(entity);
            }
            httpRequest = postMethod;
        } else if (httpMethod == HttpMethodName.PUT) {
            HttpPut putMethod = new HttpPut(url);
            httpRequest = putMethod;

            if (content != null) {
                StringEntity entity = new StringEntity(content, StandardCharsets.UTF_8);
                putMethod.setEntity(entity);
            }
        } else if (httpMethod == HttpMethodName.PATCH) {
            HttpPatch patchMethod = new HttpPatch(url);
            httpRequest = patchMethod;

            if (content != null) {
                StringEntity entity = new StringEntity(content, StandardCharsets.UTF_8);
                patchMethod.setEntity(entity);
            }
        } else if (httpMethod == HttpMethodName.GET) {
            httpRequest = new HttpGet(url);
        } else if (httpMethod == HttpMethodName.DELETE) {
            httpRequest = new HttpDelete(url);
        } else if (httpMethod == HttpMethodName.OPTIONS) {
            httpRequest = new HttpOptions(url);
        } else if (httpMethod == HttpMethodName.HEAD) {
            httpRequest = new HttpHead(url);
        } else {
            throw new UnknownHttpMethodException("Unknown HTTP method name: " + httpMethod);
        }

        httpRequest.addHeader(header);
        return httpRequest;
    }
}
